﻿using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using System.ComponentModel.Composition;
using Red.Controls.Shell;
using Microsoft.Win32;
using Red.Toolkit.Profiler.Logic;
using Red.Toolkit.Profiler.Logic.Interpreters;
using Red.Toolkit.Profiler.Drawing;
using System.Collections.ObjectModel;

namespace Red.Toolkit.Profiler
{
    [Export( typeof( IEditorInfo ) )]
    public class EditorInfo : IEditorInfo
    {
        public string Name
        {
            get { return "RED Engine Profiler"; }
        }

        public Type WindowClass
        {
            get { return typeof( ProfilerWindow ); }
        }

        public Type SettingsClass
        {
            get { return typeof( ProfilerSettings ); }
        }
    }

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class ProfilerWindow : RedEditorWindow
    {
        private ObservableCollection<ProfilerTraceInstance> _traceInstances;
        private ObservableCollection<Bookmark> _bookmarks;

        // objects common for timeline and graph components
        private TimelineStyle _timelineStyle;
        private TimelineParams _timelineParams;

        #region C-tor

        public ProfilerWindow( IEditorHost host, EditorInstanceData instanceData, EditorSharedData sharedData )
            : base( host, instanceData, sharedData )
        {
            InitializeComponent();

            _timelineStyle = new TimelineStyle();
            _timelineParams = new TimelineParams();
            _timelineParams.OnDrawDelegate = new TimelineParams.OnDraw( DrawTimelineAndGraph );

            _traceInstances = new ObservableCollection<ProfilerTraceInstance>();
            treeViewItemTraces.ItemsSource = _traceInstances;
            timelineComponent.SetUp( _traceInstances, _timelineStyle, _timelineParams );
            graphComponent.SetUp( _traceInstances, _timelineStyle, _timelineParams );

            _bookmarks = new ObservableCollection<Bookmark>();
            treeViewItemBookmarks.ItemsSource = _bookmarks;
            timelineComponent.SetBookmarksContainer( _bookmarks );

            timelineComponent.SetOnShowSelectionDetailsDelegate( new TimelineComponent.ShowSelectionDetailsDelegate( onShowSelectionDetails ) );
        }

        #endregion C-tor

        private void MenuItem_Click( object sender, RoutedEventArgs e )
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Title = "Open trace files...";
            openFileDialog.Filter =
                "All RedEngine traces|*.redjob;*.redio;*.redpc|" +
                "RedEngine IO traces|*.redio|" +
                "RedEngine job traces|*.redjob|" +
                "RedEngine perf counter traces|*.redpc";
            openFileDialog.FilterIndex = 0;
            openFileDialog.ShowReadOnly = true;
            openFileDialog.Multiselect = true;

            if ( openFileDialog.ShowDialog() == true )
                LoadTraceFiles( openFileDialog.FileNames );
        }

        private void RedWindow_Drop( object sender, DragEventArgs e )
        {
            if ( e.Data.GetDataPresent( DataFormats.FileDrop ) )
            {
                string[] files = (string[])e.Data.GetData( DataFormats.FileDrop );
                LoadTraceFiles( files );
            }
        }

        private void LoadTraceFile( string path )
        {
            ProfilerDataLoader loader = new ProfilerDataLoader();

            Stopwatch stopwatch = Stopwatch.StartNew();
            ProfilerData data = loader.LoadFromFile( path );
            stopwatch.Stop();
            Debug.Write( "Data loading: " + stopwatch.Elapsed + " sec\n" );

            if ( data == null )
            {
                MessageBox.Show( "Unable to load selected file" );
                return;
            }

            ProfilerTraceInstance trace = null;
            // find a trace with the same engine instance ID
            if ( data.EngineInstance != 0 )
                foreach ( var t in _traceInstances )
                    if ( t.EngineInstanceID == data.EngineInstance )
                    {
                        trace = t;
                        break;
                    }

            if ( trace == null )
            {
                trace = new ProfilerTraceInstance( data.EngineInstance );
                _traceInstances.Add( trace );
            }

            // create data interpreter
            ProfilerDataView view = null;
            if ( path.EndsWith( "redio" ) )
                view = new FileSystemTraceInterpreter( data, trace );
            else if ( path.EndsWith( "redjob" ) )
                view = new JobTraceInterpreter( data, trace );
            else if ( path.EndsWith( "redpc" ) )
                view = new PerfCounterTraceInterpreter( data, trace );
            else
            {
                MessageBox.Show( "Unable to interpret loaded file" );
                return;
            }

            // analyze data
            if ( !view.Prepare() )
            {
                MessageBox.Show( "Unable to process loaded file" );
                return;
            }

            trace.DataViews.Add( view );
            trace.Prepare();

            timelineComponent.AdjustView();
        }

        private void LoadTraceFiles( string[] filePaths, bool append = false )
        {
            // TODO loading splash screen
            foreach ( string path in filePaths )
            {
                LoadTraceFile( path );
            }
        }

        private void DrawTimelineAndGraph()
        {
            timelineComponent.Draw();
            graphComponent.Draw();
        }

        private void timelineComponentGrid_Loaded( object sender, RoutedEventArgs e )
        {
            timelineComponentGrid.Focus();
        }

        private void sideTreeCheckBox_Checked( object sender, RoutedEventArgs e )
        {
            DrawTimelineAndGraph();
        }

        private void sideTree_MouseDoubleClick( object sender, MouseButtonEventArgs e )
        {
            var treeView = sender as System.Windows.Controls.TreeView;
            var item = treeView.SelectedItem;

            if ( item is Bookmark )
            {
                Bookmark b = item as Bookmark;
                timelineComponent.GoToTime( b.Time );
            }
        }

        private void sideTree_KeyDown( object sender, KeyEventArgs e )
        {
            var treeView = sender as System.Windows.Controls.TreeView;
            var item = treeView.SelectedItem;

            if ( e.Key == Key.Delete )
            {
                if ( item is Bookmark )
                {
                    Bookmark b = item as Bookmark;
                    _bookmarks.Remove( b );
                    timelineComponent.Draw();
                }
                else if ( item is ProfilerDataView)
                {
                    ProfilerDataView view = item as ProfilerDataView;
                    bool redraw = view.Visible;
                    view.Trace.DataViews.Remove( view );

                    if ( redraw )
                    {
                        view.Trace.Prepare();
                        DrawTimelineAndGraph();
                    }
                }
                else if ( item is ProfilerTraceInstance )
                {
                    ProfilerTraceInstance trace = item as ProfilerTraceInstance;
                    bool redraw = trace.Visible;
                    _traceInstances.Remove( trace );

                    if ( redraw )
                        DrawTimelineAndGraph();
                }
            }
        }

        private void sideTreeDataView_Checked( object sender, RoutedEventArgs e )
        {
            var checkBox = sender as System.Windows.Controls.CheckBox;
            ProfilerDataView view = checkBox.DataContext as ProfilerDataView;

            view.Trace.Prepare();
            DrawTimelineAndGraph();
        }

        private void onShowSelectionDetails(string msg)
        {
            textBlockSelectionDetails.Text = msg;
        }

        private void ProfilerTraceInstance_DeleteClick( object sender, RoutedEventArgs e )
        {
            var item = sender as System.Windows.Controls.MenuItem;
            if ( item != null )
            {
                ProfilerTraceInstance trace = item.DataContext as ProfilerTraceInstance;
                bool redraw = trace.Visible;
                _traceInstances.Remove( trace );

                if (redraw )
                    DrawTimelineAndGraph();
            }
        }

        private void ProfilerDataView_DeleteClick( object sender, RoutedEventArgs e )
        {
            var item = sender as System.Windows.Controls.MenuItem;
            if ( item != null )
            {
                ProfilerDataView view = item.DataContext as ProfilerDataView;
                bool redraw = view.Visible;
                view.Trace.DataViews.Remove( view );

                if ( redraw )
                {
                    view.Trace.Prepare();
                    DrawTimelineAndGraph();
                }
            }
        }
    }
}
